#ifndef LEXER_HPP
#define LEXER_HPP

#include <stdio.h>
#include <string.h>
#include "parser/sourceRange.hpp"
#include "util/enumUtil.hpp"

class BufferedInputStream;

template<typename T>
class ArrayList;

#define TOKEN_TYPE(x) \
    x(INT)\
    x(STRING)\
    x(CHAR)\
    x(NAME)\
    x(LAMBDA)\
    x(LAM_DEF)\
    x(QUALIFIER)\
    x(ENDL)\
    x(ENDMARKER)\
    x(ASN)\
    x(LEFT_PAR)\
    x(RIGHT_PAR)\
    x(LEFT_BRACKET)\
    x(RIGHT_BRACKET)\
    x(LOGIC_OR)\
    x(LOGIC_AND)\
    x(LOGIC_NOT)\
    x(BIT_OR)\
    x(BIT_XOR)\
    x(BIT_AND)\
    x(LEFT_SHIFT)\
    x(RIGHT_SHIFT)\
    x(PLUS)\
    x(MINUS)\
    x(DIV)\
    x(MULT)\
    x(EQUAL)\
    x(LT)\
    x(GT)\
    x(COLON)\
    x(COMMA)\
    x(IF)\
    x(ELSE)\
    x(VAR)\
    x(TYPE)\
    x(TYPE_INT)\
    x(TYPE_BOOL)\
    x(TYPE_STRING)\
    x(TYPE_DOUBLE)\
    x(TYPE_CHAR)\
    x(TYPE_ANY)\
    x(BOOL_TRUE)\
    x(BOOL_FALSE)\
    x(ARROW)
ENUM(TokenType, TOKEN_TYPE)
#undef TOKEN_TYPE

class Token {
public:
    TokenType   _tt;
    char*       _value;
    int         _length;

    Token(TokenType tt, const char* v, int length, SourceRange range);
    ~Token();

    SourceRange sourceRange() const { return _range; };
    int cmp(const char* s);
    void print();
private:
    SourceRange _range;
};

enum State {
    INIT,
    INTEGER,
    STRING,
    CHAR,
    NAME,

    OP_EQ,
    OP_GT,
    OP_LT,
    OP_SLASH,

    OP_ADD,
    OP_SUB,
    OP_MUL,
    OP_DIV,

    OP_OR,
    OP_AND,

    OP_COLON
};

class Lexer {
private:
    BufferedInputStream* _stream;
    int     _index;
    State   _state;
    SourceLocation _location{};
    void resetLocation();

public:
    Lexer(BufferedInputStream* stream);
    ~Lexer();

    Token* filter_keyword(Token* t);
    Token* next();
    void lexSkipCharacter();
    Token* make_new_token(TokenType tt, ArrayList<char>& clist, SourceLocation begin, SourceLocation end, char c = '\0');
    char filter_char(const char* v, int size);
};

#endif
